#pragma once

#include "thread.hpp"
#include "lockGuard.hpp"
#include "log.hpp"

const int g_therad_num = 3;   // 这里规定线程池里的线程个数

// 本质还是生产消费模型
template<class T>
class ThreadPool
{
public:
    pthread_mutex_t* GetMutex()
    {
        return &_lock;
    }

    bool isEmpty()
    {
        return task_queue.empty();
    }

    void waitCond()
    {
        pthread_cond_wait(&_cond, &_lock);
    }

    T GetTask()
    {
        T t = task_queue.front();
        task_queue.pop();
        return t;
    }
public:
    ThreadPool(int thread_num = g_therad_num)
       :_num(thread_num)
    {
        pthread_mutex_init(&_lock, nullptr);
        pthread_cond_init(&_cond, nullptr);
        for (int i = 0; i <= _num; i++)
        {
            _threads.push_back(new Thread(i, rounine, this));
        }
    }

    void run()
    {
        for (auto& iter : _threads)
        {
            iter->start();
            logMessage(NORMAL, "%s %s", iter->name().c_str(), "启动成功");
        }
    }

    static void* rounine(void* args)
    {
        ThreadData* td = (ThreadData*) args;
        ThreadPool<T>* tp = (ThreadPool<T>*)td->_args;
        
        while(true)
        {
            T task;
            {
                // 类的静态成员,无法使用类的私有成员,必须写一个接口来使用
                RAII_Mutex raii_mutex(tp->GetMutex()); // 把自定定义的锁给RAII_Muex, 构造时上锁
                while(tp->isEmpty()) tp->waitCond();

                // 读取任务
                task = tp->GetTask(); 
            }
            task(td->_name);
        }
    }

    void pushTask(const T& task)
    {
        RAII_Mutex raii_mutex(&_lock);
        task_queue.push(task);
        pthread_cond_signal(&_cond);
    }

    ~ThreadPool()
    {
        for (auto& iter : _threads)
        {
            iter->join();
            delete iter;
        }
        pthread_mutex_destroy(&_lock);
        pthread_cond_destroy(&_cond);
    }
private:
    std::vector<Thread*> _threads;  // 线程池
    int _num;  // 线程池的个数
    std::queue<T> task_queue;  // 任务队列
    
    pthread_mutex_t _lock;
    pthread_cond_t _cond;
};
